/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package be.deoranjerie.ict.shibboleth;

import edu.vt.middleware.ldap.AttributesFactory;
import edu.vt.middleware.ldap.Ldap;
import edu.vt.middleware.ldap.auth.AuthenticatorConfig;
import edu.vt.middleware.ldap.auth.handler.AuthenticationCriteria;
import edu.vt.middleware.ldap.auth.handler.BindAuthenticationHandler;
import edu.vt.middleware.ldap.handler.ConnectionHandler;
import java.util.HashSet;
import java.util.Set;
import javax.naming.AuthenticationException;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author robbie.delise2
 */
public class ChangePasswordRequiredAuthenticationHandler extends BindAuthenticationHandler {
    protected final Log logger = LogFactory.getLog(this.getClass());
    
    public ChangePasswordRequiredAuthenticationHandler() {
    }
    
    public ChangePasswordRequiredAuthenticationHandler(final AuthenticatorConfig ac) {
        this.setAuthenticatorConfig(ac);
    }
    
    public void authenticate(final ConnectionHandler ch, final AuthenticationCriteria ac) throws NamingException {
        Ldap ldap = null;
        try {
            //try normal auth first
            logger.debug("Trying regular authentication");
            super.authenticate(ch, ac);
        } catch (AuthenticationException e) {
            logger.debug("Authentication failed. Checking if user has 'Force password change' flag set");
            //manual ldap connection
            ldap = new Ldap(this.config);
            //get pwdLastSet attribute. If this '0' then the flag 'Force password change' has been set in the AD.
            Attributes attr = ldap.getAttributes(ac.getDn(),
                    new String[]{"pwdLastSet"});
            //check if pwdLastSet = 0
            String pwdLastSet = (attr.get("pwdLastSet") != null) ? (String) attr.get("pwdLastSet").get() : "";
            if (pwdLastSet.equals("0")) {
                logger.debug("User has 'Force Password Change' flag set. Disabling flag and retrying authentication");
                //try setting the pwdLastSet to -1. This will allow us to do an authentication.
                this.setPwdLastSet(ldap, ac.getDn(), "-1");
                try {
                    //try authenticating with the Force Password Change disabled.
                    super.authenticate(ch, ac);
                } catch (AuthenticationException e2) {
                    logger.debug("Authentication failed with flag off. Reenabling 'Force Password Change' flag and giving up.");
                    //authentication failed, restore the pwdLastSet attribute to 0
                    this.setPwdLastSet(ldap, ac.getDn(), "0");
                    throw e2;
                }
            
                logger.debug("Authentication successfull. Reenabling 'Force Password Change' flag and throwing exception 'changepasswordrequired'.");
                //Authentication successfull. Restore the pwdLastSet attribute to 0
                this.setPwdLastSet(ldap, ac.getDn(), "0");
                //Throw a customized namingexception that will be usefull for login.jsp to send us to a password change page.
                throw new AuthenticationException(String.format("ChangePasswordRequired;%s", ac.getDn()));
            }
            //if flag not set, don't forget to deny login if the authentication failed originaly.
            logger.debug("No 'Force Password Change' flag found. Throwing original exception");
            throw e;
            
        } finally {
            if (ldap != null)
                ldap.close();
        }
    }
    
    private void setPwdLastSet(Ldap ldap, String dn, String value) throws NamingException {
        try {
            ldap.modifyAttributes(dn, Ldap.AttributeModification.REPLACE,
                AttributesFactory.createAttributes("pwdLastSet", value));
        } catch(NamingException e){
            logger.debug(String.format("Could not change 'pwdLastSet' to '%s'. Are you sure you have write permissions in LDAP ?", value));
            logger.debug(e.getMessage());
            throw e;
        }
    }
    
    public ChangePasswordRequiredAuthenticationHandler newInstance() {
        return new ChangePasswordRequiredAuthenticationHandler(this.config);
    }
}
